package com.bc.commonentity.log;

import com.bc.commoncore.config.ConfigBase;
import com.bc.commoncore.util.JsonUtil;
import com.bc.commoncore.util.ParseHelper;
import com.bc.commoncore.util.StringUtils;
import com.bc.commoncore.util.SystemUtils;
import com.bc.commonentity.common.ActionDetailLog;
import com.bc.commonentity.common.AjaxNotLoginRunTimeException;
import com.bc.commonentity.common.InterceptorNotLoginRunTimeException;
import com.bc.commonentity.common.JsonResult;
import com.bc.commonentity.enums.HttpReturnRnums;
import com.bc.commonentity.enums.LogDetailType;
import com.dianping.cat.Cat;
import com.dianping.cat.message.Transaction;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.jaxrs.impl.ResponseImpl;
import org.apache.cxf.message.Exchange;
import org.apache.cxf.message.Message;
import org.apache.cxf.message.MessageContentsList;
import org.apache.cxf.phase.PhaseInterceptorChain;
import org.apache.cxf.transport.http.AbstractHTTPDestination;
import org.apache.ibatis.session.SqlSessionFactory;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.method.HandlerMethod;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

public class AspectLogHelper {
	protected static final Logger logger = LoggerFactory.getLogger(AspectLogHelper.class);
    public static Object daoAspectDetail(ProceedingJoinPoint point,SqlSessionFactory readOnlyMySqlSessionFactory) throws Throwable{
    	String sql=MybatisSqlHelper.before(point,readOnlyMySqlSessionFactory);
    	logger.debug("最终Sql:"+sql);
		try {
			Object[] args=ParseHelper.filterEmojiBySql(point.getArgs(),sql,"*");//point.getArgs();
			return aspectDetail(point, LogDetailType.db, args,sql);
		}finally {
			MybatisSqlHelper.after(point);
		}
	}

    public static Object aspectDetail(ProceedingJoinPoint point,LogDetailType logDetailType) throws Throwable{
    	return aspectDetail(point, logDetailType, null,"");
    }
	public static Object aspectDetail(ProceedingJoinPoint point,
									LogDetailType logDetailType,
									Object[] args,
									String sql) throws Throwable{
		if (ConfigBase.getIsOpenESLog().equals("0")) {
			if (args!=null) {
				return point.proceed(args);
			}
			return point.proceed();
		}
	    Date beginTime=new Date();
	    Object retVal=null;
		String stackTrace="";
		MethodSignature signature = (MethodSignature) point.getSignature();
		String methodName =signature.getDeclaringTypeName() + "." + signature.getMethod().getName();

		ActionDetailLog temp=new ActionDetailLog();
		temp.setMethodName(methodName);
		temp.setOptType(logDetailType.value());
		temp.setParam(LogDetailHelper.getJsonParam(point.getArgs()));
		Transaction catTrans= null;
		Map<String, String> catMessageIdsMap=null;
		try{
			//机器环境有问题时,newTransaction会引发空引用，因此这里吞掉了异常
			catTrans=Cat.newTransaction(logDetailType.desc(), LogDetailHelper.getRequestKey(temp));
			catMessageIdsMap=LogDetailHelper.setAttachment(logDetailType);
		}catch (Exception e){
			logger.error("cat初始化失败");
		}
		try {
			if (args!=null) {
				retVal=point.proceed(args);
			}else {
				retVal = point.proceed();
			}
		}catch(Exception e){
			stackTrace=e.getMessage()+StringUtils.getStackTrace(e);
			throw e;
		}  finally {
			// 记录方法调用详细信息
			long executeTime = System.currentTimeMillis() - beginTime.getTime();
			String resultJson="";
			if(catTrans!=null){
				if(logDetailType==LogDetailType.postget){
					LogDetailHelper.pigeonCall(catTrans);
					//如果是通过postget访问非java接口时才记录返回值
					if(retVal!=null&&
							retVal instanceof String&&
							!catTrans.getName().contains("/services/")){
						resultJson=(String)retVal;
					}
				}
				catTrans.setStatus(LogDetailHelper.getCauseBy(null,stackTrace));
				catTrans.complete();
			}
			LogDetailHelper.addLogDetail(methodName, "", point.getArgs(),
					beginTime, executeTime,stackTrace, sql,resultJson,logDetailType,catMessageIdsMap);
		}
	    return retVal;
	}

	public static void springAfterCompletion(HttpServletRequest request,
			Object handler, Exception ex)
			throws Exception {
    	try {
			if (handler instanceof HandlerMethod) {
				if (!LogDetailHelper.needCheck(request.getServletPath())) {
					return;
				}
				HandlerMethod handlerMethod = (HandlerMethod) handler;

				String methodName = handlerMethod.getMethod().toString();
				String param = JsonUtil.obj2string(request.getParameterMap());
				int userID=ParseHelper.ToInt(request.getAttribute("userID"),-1);
				String userName = ParseHelper.ToString(request.getAttribute("userName"),"");

				String headers = JsonUtil.obj2string(LogDetailHelper.getHeadersInfo(request));
				String stackTrace = "";
				// 记录系统异常
				Object obj = request.getAttribute("stackTrace");
				if (obj != null) {
					Object objMsg = request.getAttribute("exception");
					stackTrace = (objMsg == null ? "" : objMsg.toString())+obj.toString();
				} else if (ex != null){
					stackTrace = getStackTrace(ex);
				}
				String clientIp = SystemUtils.getClientIp(request);
				String strRequestType = request.getHeader("X-Requested-With");
				int requestType=0;
				if (strRequestType != null && strRequestType.equals("XMLHttpRequest")) {
					requestType=1;
				}
				LogDetailHelper.systemActionLog(userID,userName,requestType,
						clientIp,request.getRequestURL().toString(),param,
						"",request.getContentType(),headers,
						request.getMethod(),methodName,"",
						stackTrace,"accessUrl");
			}
		}catch (Exception e){
			logger.error(request.getRequestURL().toString()+"springAfterCompletion时出错:"+e.getMessage()+StringUtils.getStackTrace(e));
			ErrorEmail.sendErrorMail(request.getRequestURL().toString()+"springAfterCompletion时出错",e);
		}

	}
	private static String getStackTrace(Exception ex){
		if(ex==null){
			return "";
		}
		if(ex instanceof AjaxNotLoginRunTimeException||
				ex instanceof InterceptorNotLoginRunTimeException){
			return "";
		}
		if(ex.getCause()!=null){
			if(ex.getCause() instanceof AjaxNotLoginRunTimeException||
					ex.getCause() instanceof InterceptorNotLoginRunTimeException){
				return "";
			}
		}
		return (ex.getMessage() == null ? "" : ex.getMessage())+StringUtils.getStackTrace(ex);
	}
	/**
	 * app端的MsgInterceptor类中抛出异常时，需要调用此方法结束cat的Transaction，否则会导致cat调用链混乱
	 */
	public static void cxfAfterCompletionOnerror(String stackTrace){
		String url="";
		try{
			Message message = PhaseInterceptorChain.getCurrentMessage();
			if(message==null){
				return;
			}
			Exchange exchange = message.getExchange();
			Message inMessage = exchange.getInMessage();

			url = (String) inMessage.get(Message.REQUEST_URL);
			if(!LogDetailHelper.needCheck(url)){
				return;
			}

			TreeMap header = (TreeMap) inMessage.get(Message.PROTOCOL_HEADERS);
			String contentType = (String) inMessage.get(Message.CONTENT_TYPE);
			String httpRequestMethod = (String) inMessage.get(Message.HTTP_REQUEST_METHOD);
			HttpServletRequest request = (HttpServletRequest)inMessage.get(AbstractHTTPDestination.HTTP_REQUEST);// 这句可以获取到request
			String clientIp = SystemUtils.getClientIp(request);

			String param ="";
			String decryptMsg ="";
			if(exchange.containsKey("encryptMsg")){
				param =(String) exchange.get("encryptMsg");
			}
			if(exchange.containsKey("decryptMsg")){
				decryptMsg =(String) exchange.get("decryptMsg");
			}
			LogDetailHelper.systemActionLog(-1,"",0,
					clientIp,url,param,
					decryptMsg,contentType,header.toString(),
					httpRequestMethod,"","",
					stackTrace,"accessUrl");
		}catch (Exception e){
			logger.error(url+"cxfAfterCompletionOnerror时出错:"+e.getMessage()+StringUtils.getStackTrace(e));
			ErrorEmail.sendErrorMail(url+"cxfAfterCompletionOnerror时出错",e);
		}

	}
	public static void cxfAfterCompletion(Message message) throws Fault {
		String url="";
    	try{
			Exchange exchange = message.getExchange();
			Message inMessage = exchange.getInMessage();
			Message outMessage = exchange.getOutMessage();

			if(outMessage==null){
				return;
			}
			//checkParam(method);
			String stackTrace = "";
			String resultJson = "";
			url = (String) inMessage.get(Message.REQUEST_URL);
			if(!LogDetailHelper.needCheck(url)){
				return;
			}
			MessageContentsList messageContentsList = (MessageContentsList) outMessage.getContent(List.class);
			//系统异常时，返回值的类型为ResponseImpl
			if (messageContentsList.get(0) instanceof ResponseImpl) {
				Object responseEntity=((ResponseImpl)messageContentsList.get(0)).getEntity();
				if (responseEntity instanceof JsonResult) {
					JsonResult res = (JsonResult)responseEntity;
					if (res.getCode() == HttpReturnRnums.SystemError.value()) {
						stackTrace = res.getMsg()+(res.getData()==null?"":res.getData().toString());
					}
				}
			}else if (messageContentsList.get(0) instanceof JsonResult) {
				//正常返回时，返回值的类型为HttpResultModel<Object>)
				//此时需要记录返回值的json
				try {
					//请求的返回值可能很大，会导致序列化非常耗时，因此这里注释掉
					//resultJson = JsonUtil.obj2string(messageContentsList.get(0));
				} catch (Exception e) {
					stackTrace = e.getMessage()+StringUtils.getStackTrace(e);
					logger.error(stackTrace);
					//如果当前请求返回的结果没问题，但是序列化时出错了，
					//此时需要将outMessage.messageContentsList中的正确的方法返回值改为异常对象
					//否则，由于cxf框架在序列化时
					//是根据outMessage.messageContentsList转换为json输出流+GlobalExceptionHandler中返回的Response的json输出流
					//而outMessage.messageContentsList无法正确序列化，导致输出流中只输出了部分json（不包含出错的节点）
					//例如：一个方法返回值中的data对象中有两个属性total和count，而count属性导致了序列化失败，则最终输出的json：
					//{"code": 200,"msg": "success","data": {"total": 5}}{"code": -1,"msg": "系统错误","data": "堆栈信息。。"}
					//而不是期望的：{"code": -1,"msg": "系统错误","data": "堆栈信息。。"}

					JsonResult rep=new JsonResult();
					rep.setCode(HttpReturnRnums.SystemError.value());
					rep.setMsg(HttpReturnRnums.SystemError.desc());
					rep.setData(stackTrace);
					messageContentsList.remove(0);
					messageContentsList.add(0, rep);
				}
			}

			TreeMap header = (TreeMap) inMessage.get(Message.PROTOCOL_HEADERS);
			String contentType = (String) inMessage.get(Message.CONTENT_TYPE);
			String httpRequestMethod = (String) inMessage.get(Message.HTTP_REQUEST_METHOD);
			HttpServletRequest request = (HttpServletRequest)inMessage.get(AbstractHTTPDestination.HTTP_REQUEST);// 这句可以获取到request
			String clientIp = SystemUtils.getClientIp(request);

			String param ="";
			String decryptMsg ="";
			if(exchange.containsKey("encryptMsg")){
				param =(String) exchange.get("encryptMsg");
			}
			if(exchange.containsKey("decryptMsg")){
				decryptMsg =(String) exchange.get("decryptMsg");
			}
			List<Object> paramList = (List<Object>) inMessage.getContent(List.class);
			if (paramList!=null&&
					paramList.size()>0&&
					paramList.get(0)!=null) {
				param=JsonUtil.obj2string(paramList.get(0));
				decryptMsg="";
			}

			LogDetailHelper.systemActionLog(-1,"",0,
					clientIp,url,param,
					decryptMsg,contentType,header.toString(),
					httpRequestMethod,"",resultJson,
					stackTrace,"accessUrl");
		}catch (Exception e){
			logger.error(url+"cxfAfterCompletion时出错:"+e.getMessage()+StringUtils.getStackTrace(e));
			ErrorEmail.sendErrorMail(url+"cxfAfterCompletion时出错",e);
		}

	}

	public static void cxfBefore(Message message){
		Exchange exchange = message.getExchange();
		Message inMessage = exchange.getInMessage();
		String url = (String) inMessage.get(Message.REQUEST_URL);
		LogDetailHelper.initCatMessageTree(url,"cxf");
	}
	public static boolean springBefore(HttpServletRequest request, Object handler){
		if (handler instanceof HandlerMethod) {
			LogDetailHelper.initCatMessageTree(request.getRequestURL().toString(),"springmvc");
		}
		return true;
	}
}
